package com.example.javapoet.demo.intermediate;

import com.squareup.javapoet.*;

import javax.lang.model.element.Modifier;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

/**
 * JavaPoet中级示例 - 生成带有Javadoc注释的代码
 */
public class JavadocGeneration {

    public static void main(String[] args) {
        // 生成带有Javadoc的类
        generateClassWithJavadoc();

        // 生成带有详细Javadoc的方法
        generateMethodsWithDetailedJavadoc();

        // 生成带有Javadoc标签的代码
        generateCodeWithJavadocTags();
    }

    /**
     * 生成带有详细Javadoc的方法
     */
    public static void generateMethodsWithDetailedJavadoc() {
        // 创建方法
        MethodSpec calculateMethod = MethodSpec.methodBuilder("calculate")
                .addModifiers(Modifier.PUBLIC)
                .returns(double.class)
                .addParameter(double.class, "principal")
                .addParameter(double.class, "rate")
                .addParameter(int.class, "years")
                .addParameter(boolean.class, "compound")
                .addJavadoc("计算投资回报\n")
                .addJavadoc("\n")
                .addJavadoc("<p>此方法根据提供的参数计算投资的最终价值。</p>\n")
                .addJavadoc("\n")
                .addJavadoc("<pre>\n")
                .addJavadoc("  // 示例用法:\n")
                .addJavadoc("  double result = calculator.calculate(1000.0, 0.05, 10, true);\n")
                .addJavadoc("</pre>\n")
                .addJavadoc("\n")
                .addJavadoc("计算公式如下:\n")
                .addJavadoc("<ul>\n")
                .addJavadoc("  <li>简单利息: {@code principal * (1 + rate * years)}</li>\n")
                .addJavadoc("  <li>复利: {@code principal * Math.pow(1 + rate, years)}</li>\n")
                .addJavadoc("</ul>\n")
                .addJavadoc("\n")
                .addJavadoc("@param principal 本金金额\n")
                .addJavadoc("@param rate 年利率（如5%应输入0.05）\n")
                .addJavadoc("@param years 投资年限\n")
                .addJavadoc("@param compound 是否使用复利计算\n")
                .addJavadoc("@return 投资的最终价值\n")
                .addJavadoc("@throws IllegalArgumentException 如果本金为负或利率小于0\n")
                .beginControlFlow("if (principal < 0 || rate < 0)")
                .addStatement("throw new $T($S)", IllegalArgumentException.class, "本金和利率必须为非负数")
                .endControlFlow()
                .beginControlFlow("if (compound)")
                .addStatement("return principal * Math.pow(1 + rate, years)")
                .nextControlFlow("else")
                .addStatement("return principal * (1 + rate * years)")
                .endControlFlow()
                .build();

        MethodSpec parseDataMethod = MethodSpec.methodBuilder("parseData")
                .addModifiers(Modifier.PUBLIC)
                .addTypeVariable(TypeVariableName.get("T"))
                .returns(TypeVariableName.get("T"))
                .addParameter(String.class, "data")
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get("java.util.function", "Function"),
                        ClassName.get(String.class),
                        TypeVariableName.get("T")), "parser")
                .addJavadoc("解析数据并转换为指定类型\n")
                .addJavadoc("\n")
                .addJavadoc("此方法接受一个字符串数据和一个解析函数，将数据转换为所需类型。\n")
                .addJavadoc("如果数据为null或空，则返回null。\n")
                .addJavadoc("\n")
                .addJavadoc("示例用法:\n")
                .addJavadoc("\n")
                .addJavadoc("<pre>{@code\n")
                .addJavadoc("// 解析为整数\n")
                .addJavadoc("Integer value = parseData(\"123\", Integer::parseInt);\n")
                .addJavadoc("\n")
                .addJavadoc("// 解析为自定义对象\n")
                .addJavadoc("Person person = parseData(jsonString, json -> objectMapper.readValue(json, Person.class));\n")
                .addJavadoc("}</pre>\n")
                .addJavadoc("\n")
                .addJavadoc("@param <T> 目标类型\n")
                .addJavadoc("@param data 要解析的数据字符串\n")
                .addJavadoc("@param parser 将字符串转换为目标类型的函数\n")
                .addJavadoc("@return 解析后的对象，如果输入为null或空则返回null\n")
                .addJavadoc("@throws RuntimeException 如果解析过程中发生错误\n")
                .beginControlFlow("if (data == null || data.isEmpty())")
                .addStatement("return null")
                .endControlFlow()
                .beginControlFlow("try")
                .addStatement("return parser.apply(data)")
                .nextControlFlow("catch (Exception e)")
                .addStatement("throw new $T($S + e.getMessage(), e)", RuntimeException.class, "解析数据时出错: ")
                .endControlFlow()
                .build();

        // 创建类
        TypeSpec utilityClass = TypeSpec.classBuilder("DataUtility")
                .addModifiers(Modifier.PUBLIC)
                .addJavadoc("数据处理工具类\n")
                .addJavadoc("\n")
                .addJavadoc("此类提供了各种数据处理和计算的实用方法。\n")
                .addJavadoc("\n")
                .addJavadoc("@author JavaPoet示例\n")
                .addJavadoc("@since 1.0\n")
                .addMethod(calculateMethod)
                .addMethod(parseDataMethod)
                .build();

        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.javadoc", utilityClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成DataUtility类（带详细Javadoc）:");
            javaFile.writeTo(System.out);

            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成DataUtility.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成带有Javadoc标签的代码
     */
    public static void generateCodeWithJavadocTags() {
        // 创建方法
        MethodSpec processMethod = MethodSpec.methodBuilder("processData")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "data")
                .addJavadoc("处理数据列表\n")
                .addJavadoc("\n")
                .addJavadoc("此方法处理输入的字符串列表，并返回处理后的结果。\n")
                .addJavadoc("\n")
                .addJavadoc("{@code\n")
                .addJavadoc("  List<String> input = Arrays.asList(\"a\", \"b\", \"c\");\n")
                .addJavadoc("  List<String> result = processData(input);\n")
                .addJavadoc("}\n")
                .addJavadoc("\n")
                .addJavadoc("@param data 要处理的数据列表\n")
                .addJavadoc("@return 处理后的数据列表\n")
                .addJavadoc("@see java.util.List\n")
                .addJavadoc("@since 1.2\n")
                .addJavadoc("@deprecated 使用 {@link #processDataV2(List)} 代替\n")
                .addStatement("return data.stream().map(String::toUpperCase).toList()")
                .build();

        MethodSpec processV2Method = MethodSpec.methodBuilder("processDataV2")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)))
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(List.class),
                        ClassName.get(String.class)), "data")
                .addJavadoc("处理数据列表（改进版）\n")
                .addJavadoc("\n")
                .addJavadoc("此方法是{@link #processData(List)}的改进版本，提供了更好的性能和更多功能。\n")
                .addJavadoc("\n")
                .addJavadoc("@param data 要处理的数据列表\n")
                .addJavadoc("@return 处理后的数据列表\n")
                .addJavadoc("@throws NullPointerException 如果输入为null\n")
                .addJavadoc("@see java.util.stream.Stream\n")
                .addStatement("return data.stream().map(String::toUpperCase).filter(s -> !s.isEmpty()).toList()")
                .build();

        // 创建类
        TypeSpec processorClass = TypeSpec.classBuilder("DataProcessor")
                .addModifiers(Modifier.PUBLIC)
                .addJavadoc("数据处理器类\n")
                .addJavadoc("\n")
                .addJavadoc("此类提供了处理数据的各种方法。\n")
                .addJavadoc("\n")
                .addJavadoc("<p>示例用法:</p>\n")
                .addJavadoc("<pre>\n")
                .addJavadoc("  DataProcessor processor = new DataProcessor();\n")
                .addJavadoc("  List<String> result = processor.processDataV2(inputList);\n")
                .addJavadoc("</pre>\n")
                .addJavadoc("\n")
                .addJavadoc("@author JavaPoet示例\n")
                .addJavadoc("@version 2.0\n")
                .addMethod(processMethod)
                .addMethod(processV2Method)
                .build();

        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.javadoc", processorClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成DataProcessor类（带Javadoc标签）:");
            javaFile.writeTo(System.out);

            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成DataProcessor.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成带有Javadoc的类
     */
    public static void generateClassWithJavadoc() {
        // 创建字段
        FieldSpec idField = FieldSpec.builder(long.class, "id")
                .addModifiers(Modifier.PRIVATE)
                .addJavadoc("用户唯一标识符\n")
                .build();

        FieldSpec nameField = FieldSpec.builder(String.class, "name")
                .addModifiers(Modifier.PRIVATE)
                .addJavadoc("用户姓名\n")
                .build();

        FieldSpec emailField = FieldSpec.builder(String.class, "email")
                .addModifiers(Modifier.PRIVATE)
                .addJavadoc("用户电子邮件地址\n")
                .build();

        FieldSpec activeField = FieldSpec.builder(boolean.class, "active")
                .addModifiers(Modifier.PRIVATE)
                .addJavadoc("用户账户是否激活\n")
                .build();

        // 创建构造函数
        MethodSpec constructor = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(long.class, "id")
                .addParameter(String.class, "name")
                .addParameter(String.class, "email")
                .addJavadoc("创建一个新的用户对象\n")
                .addJavadoc("\n")
                .addJavadoc("@param id 用户ID\n")
                .addJavadoc("@param name 用户姓名\n")
                .addJavadoc("@param email 用户电子邮件\n")
                .addStatement("this.id = id")
                .addStatement("this.name = name")
                .addStatement("this.email = email")
                .addStatement("this.active = false")
                .build();

        // 创建方法
        MethodSpec getIdMethod = MethodSpec.methodBuilder("getId")
                .addModifiers(Modifier.PUBLIC)
                .returns(long.class)
                .addJavadoc("获取用户ID\n")
                .addJavadoc("\n")
                .addJavadoc("@return 用户ID\n")
                .addStatement("return id")
                .build();

        MethodSpec getNameMethod = MethodSpec.methodBuilder("getName")
                .addModifiers(Modifier.PUBLIC)
                .returns(String.class)
                .addJavadoc("获取用户姓名\n")
                .addJavadoc("\n")
                .addJavadoc("@return 用户姓名\n")
                .addStatement("return name")
                .build();

        MethodSpec getEmailMethod = MethodSpec.methodBuilder("getEmail")
                .addModifiers(Modifier.PUBLIC)
                .returns(String.class)
                .addJavadoc("获取用户电子邮件\n")
                .addJavadoc("\n")
                .addJavadoc("@return 用户电子邮件\n")
                .addStatement("return email")
                .build();

        MethodSpec isActiveMethod = MethodSpec.methodBuilder("isActive")
                .addModifiers(Modifier.PUBLIC)
                .returns(boolean.class)
                .addJavadoc("检查用户账户是否激活\n")
                .addJavadoc("\n")
                .addJavadoc("@return 如果账户已激活则返回true，否则返回false\n")
                .addStatement("return active")
                .build();

        MethodSpec activateMethod = MethodSpec.methodBuilder("activate")
                .addModifiers(Modifier.PUBLIC)
                .returns(void.class)
                .addJavadoc("激活用户账户\n")
                .addStatement("this.active = true")
                .build();

        MethodSpec deactivateMethod = MethodSpec.methodBuilder("deactivate")
                .addModifiers(Modifier.PUBLIC)
                .returns(void.class)
                .addJavadoc("停用用户账户\n")
                .addStatement("this.active = false")
                .build();

        // 创建类
        TypeSpec userClass = TypeSpec.classBuilder("User")
                .addModifiers(Modifier.PUBLIC)
                .addJavadoc("用户类表示系统中的一个用户账户\n")
                .addJavadoc("\n")
                .addJavadoc("该类包含用户的基本信息，如ID、姓名、电子邮件等，\n")
                .addJavadoc("并提供了管理用户账户状态的方法。\n")
                .addJavadoc("\n")
                .addJavadoc("@author JavaPoet示例\n")
                .addJavadoc("@version 1.0\n")
                .addField(idField)
                .addField(nameField)
                .addField(emailField)
                .addField(activeField)
                .addMethod(constructor)
                .addMethod(getIdMethod)
                .addMethod(getNameMethod)
                .addMethod(getEmailMethod)
                .addMethod(isActiveMethod)
                .addMethod(activateMethod)
                .addMethod(deactivateMethod)
                .build();

        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.javadoc", userClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("生成User类（带Javadoc）:");
            javaFile.writeTo(System.out);

            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成User.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}